<?
require_once('dpvr.config.php');
if($config['twitter']) {
  require_once('classes/twitter.class.php');
  $twitter = new Twitter($config['twitter_username'], $config['twitter_password']);
}

function downloadShow($myShow, $season, $episode, $airdate=0) {
  global $config;

  $curAirdate = 0;
  
  // if we're daily then get the air date from tvdb
  if ((int)$myShow->daily == 1) {
    if (empty($airdate)) {
      if ($myShow->getTVDB() == null) {
        print_log("  ERROR: Unable to translate episode into date because TVDB is down, skipping for now");
        return array(false, '', '');
      }
      $curEp = $myShow->showTVDB->getEpisode($season, $episode);
      $curAirdate = $curEp->firstAired;
    } else
      $curAirdate = $airdate;
  }

  // try to find an NZB
  $searchResult = findNZB($myShow, $season, $episode, $curAirdate);
  $mailString = '';

  // if we didn't find anything then return
  if ($searchResult === false)
    return false;
  
  if ($searchResult['nzb_source'] == 'tvnzb' || $searchResult['nzb_source'] == 'tvbinz')
    $title = $searchResult['nzb_title'];
  else
    $title = false;

  // send the NZB to the correct place
  switch($config['nzb_handler']) {
    case 'nzb':
      $downloadResult = downloadNZB($searchResult['nzb'], $title, $searchResult['nzb_source']);
      break;
    case 'sabnzbd':
      $downloadResult = sendToSAB($searchResult['nzb'], $title);
      break;
    case 'hellanzb':
      $downloadResult = sendToHella($searchResult['nzb'], $title);
      break;
    default:
      print_log("  ERROR: Unknown NZB handler specified in your config, not downloading");
      break;
  }
  
  if ($downloadResult !== false) 
    $mailString = postDownload($myShow, $season, $episode, $airdate, $searchResult['correct_format']);
  else
    return false;

  return array($downloadResult, $searchResult['nzb_source'], $mailString);
}

// figures out how and where to search for an NZB and then tries to find one
function findNZB ($show, $season, $episode, $airdate) {
  global $config;

  print_log('  Searching for '.$season.'x'.$episode);
  
  $nzb_result = false;
  $correct_format = false;
  $nzb_source = false;
  
  if ($config['newzbin']) {

    // try searching for the correct format first, if we can't find it then try any format if dfa
    $nzb_result = search_newzbin($show, $season, $episode, $airdate, $show->format);
    if ($nzb_result) {
      $nzb_source = 'newzbin';
      $correct_format = true;
    } else if ($show->dfa == 1 && $show->dfa_waiting != 1) {
      print_log("    couldn't find correct format, checking for first available");
      $nzb_result = search_newzbin($show, $season, $episode, $airdate, '');
      if ($nzb_result) {
        $nzb_source = 'newzbin';
        $correct_format = false;
      }
    }
  }

  // try tvnzb
  if (!$nzb_source && $config['tvnzb']) {

    // try searching for the correct format first, if we can't find it then try any format if dfa
    $nzb_result = search_tvnzb($show, $season, $episode, $airdate, $show->format);
    if ($nzb_result) {
      $nzb_source = 'tvnzb';
      $correct_format = true;
    } else if ($show->dfa == 1 && $show->dfa_waiting != 1) {
      print_log("    couldn't find correct format, trying first available");
      $nzb_result = search_tvnzb($show, $season, $episode, $airdate, '');
      if ($nzb_result) {
        $nzb_source = 'tvnzb';
        $correct_format = false;
      }
    }
  }

  // try tvbinz
  if (!$nzb_source && $config['tvbinz']) {

    // try searching for the correct format first, if we can't find it then try any format if dfa
    $nzb_result = search_tvbinz($show, $season, $episode, $airdate, $show->format);
    if ($nzb_result) {
      $nzb_source = 'tvbinz';
      $correct_format = true;
    } else if ($show->dfa == 1 && $show->dfa_waiting != 1) {
      print_log("    couldn't find correct format, trying first available");
      $nzb_result = search_tvbinz($show, $season, $episode, $airdate, '');
      if ($nzb_result) {
        $nzb_source = 'tvbinz';
        $correct_format = false;
      }
    }
  }
  
  if ($nzb_source !== false)
    return array('nzb' => $nzb_result['id'], 'nzb_title' => $nzb_result['title'], 'nzb_source' => $nzb_source, 'correct_format' => $correct_format);
  else
    return false;
}

function search_newzbin($show, $season, $episode, $airdate, $format) {
  global $config;
  
  //newzbin info blank
  if(empty($config['newzbin_username']) || empty($config['newzbin_password'])) {
    print_log('    ERROR: newzbin config values not set!');
    return false;
  }

  $smallName = substr($show->seriesName.'-', 0, strrpos($show->seriesName, "(")-1);
  
  // use SxEE for most shows but some use YYYY-MM-DD
  if ($airdate != 0)
    $epIdentifier = date('Y-m-d', $airdate);
  else
    $epIdentifier = $season.'x'.sprintf('%02d', $episode);
  
  // try our best to be specific if we have no tvrage URL... if we do have one then we don't need to worry about it
  if (empty($show->tvrageurl))
    $q = '"'.$show->seriesName.' - '.$epIdentifier.'" OR "'.$smallName.' - '.$epIdentifier.'"';
  else
    $q = $smallName.' - '.$epIdentifier;
    
  $q_debug = $show->seriesName . ' - ' .$epIdentifier. ' (' . $GLOBALS['formats'][strval($format)] . ')';
  print_log('    searching newzbin for ' . $q_debug);
  
  //build up params
  $query = array(
    'q=^' . urlencode($q),
    'u_v3_retention=' . ($config['ng_retention'] * 24 * 60 * 60),
    'searchaction=Search',
    'fpn=p',
    'category=8',
    'area=-1',
    'u_nfo_posts_only=0',
    'u_url_posts_only=0',
    'u_comment_posts_only=0',
    'sort=ps_edit_date',
    'order=desc',
    'areadone=-1',
    'feed=csv'
  );

  if ($show->tvrageurl != '')
    $query[] = 'q_url='.urlencode($show->tvrageurl);
	
  if(!empty($format)) {
    $query[] = 'ps_rb_video_format=' . $format;
  }
	
  //send to newzbin
  if($fp = @fopen('http://v3.newzbin.com/search/?' . implode('&', $query), 'r')) {
    $line = @fgetcsv($fp);
    //var_dump($line);
    //return false;
		
    /*
    0: posted date
    1: nzb id
    2: nzb title
    3: newzbin url
    4: tv url
    5: newsgroup names (separated by +)
    */
		
    //newzbin id found
    if($line[1] > 0) {
      print_log('      found nzb ID ' . $line[1]);

      //check that it's the right show by comparing the URL
      if (stristr($line[4], $show->tvrageurl) !== false) {
        print_log('      false positive, skipping');
        return false;
      } else
        return array('id' => $line[1], 'title' => $line[2]);

    }
    //newzbin id not found
    else {
      print_log('      nzb ID not found');
      return false;
    }
  }
	
}

// search tvnzb feeds for the show
function search_tvnzb($show, $season, $episode, $airdate, $format) {

  global $configData, $GLOBALS, $config;

  if (empty($config['curl_timeout']))
    $timeout = 30;
  else
    $timeout = $config['curl_timeout'];

  # remove bad characters and replace spaces
  $name = str_replace(" ", "\.", $show->seriesName);
  $smallName = substr($name.'-', 0, strrpos($name, "(")-1);
  $goodShow = sanitize_string(str_replace("&", "and", $smallName), ":()'");

  // make our regex to find the episode with
  $startMatchString = "/.*".$goodShow.".*\.";
  if ($airdate != 0) {
    $epMatchStrings[] = date("Y\.m\.d", $airdate);
    $epMatchStrings[] = date("m\.d\.Y", $airdate);
    $debugEpString = date("Y\.m\.d", $airdate);
  } else {
    $epMatchStrings[] = 'S' . sprintf('%02d', $season) . 'E' . sprintf('%02d', $episode);
    $debugEpString = 'S' . sprintf('%02d', $season) . 'E' . sprintf('%02d', $episode);
  }
  $endMatchString = "\..*".$GLOBALS['formats'][strval($format)].".*/i";

  $q_debug = stripslashes($goodShow).'*.'.$debugEpString . '.*' . $GLOBALS['formats'][strval($format)] . '';
  print_log('    searching tvnzb for ' . $q_debug);

  $tvnzb_ch = curl_init($configData['tvnzb_rss']);
  curl_setopt($tvnzb_ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  curl_setopt($tvnzb_ch, CURLOPT_RETURNTRANSFER, true);
  $tvnzb_result = curl_exec($tvnzb_ch);
  curl_close($tvnzb_ch);

  // get the feed
  $feed_xml = simplexml_load_string($tvnzb_result);

  // search every show
  foreach ($feed_xml->channel[0]->item as $myItem) {

    // match the nzb name with our regexes
    foreach ($epMatchStrings as $curEpMatchStr) {
      $found = preg_match($startMatchString.$curEpMatchStr.$endMatchString, $myItem->title);
      if ($found)
        break;
    }

    // if we found it then return the necessary info
    if ($found) {
      print_log("      found an nzb $myItem->title at $myItem->link");
      return array('id' => $myItem->link, 'title' => $myItem->description);
    }
  }

  print_log('      show not found on rss feed from tvnzb.com');
  return false;
}

// search tvbinz feeds for the show
function search_tvbinz($show, $season, $episode, $airdate, $format) {

  global $config, $configData, $GLOBALS;

  //newzbin info blank
  if(empty($config['tvbinz_uid']) || empty($config['tvbinz_hash'])) {
    print_log('    ERROR: tvbinz config values not set!');
    return false;
  }

  if (empty($config['curl_timeout']))
    $timeout = 30;
  else
    $timeout = $config['curl_timeout'];

  # remove bad characters and replace spaces, take (xxx) off the end (tvbinz is never going to have episodes of some random duplicate show coming up)
  $name = str_replace(" ", "\.", $show->seriesName);
  $smallName = substr($name.'-', 0, strrpos($name, "(")-1);
  $goodShow = sanitize_string(str_replace("&", "and", $smallName), ":()'");

  // take advantage of the built in feed search an build a search string to narrow it down a bit
  $searchStr = $goodShow.".".$GLOBALS['formats'][strval($format)].

  // get the feed
  $tvbinz_ch = curl_init($configData['tvbinz_rss'].'?i='.$config['tvbinz_uid'].'&h='.$config['tvbinz_hash']."&search=".$searchStr);
  curl_setopt($tvbinz_ch, CURLOPT_CONNECTTIMEOUT, $timeout);
  curl_setopt($tvbinz_ch, CURLOPT_ENCODING, 'gzip');
  curl_setopt($tvbinz_ch, CURLOPT_COOKIE, 'uid='.$config['tvbinz_uid'].'; hash='.$config['tvbinz_hash'].';');
  curl_setopt($tvbinz_ch, CURLOPT_RETURNTRANSFER, true);
  $tvbinz_result = curl_exec($tvbinz_ch);
  curl_close($tvbinz_ch);

  // make our regex to find the episode with
  $startMatchString = "/.*".$goodShow.".*\.";
  if ($airdate != 0) {
    $epMatchStrings[] = date("Y\.m\.d", $airdate);
    $epMatchStrings[] = date("m\.d\.Y", $airdate);
    $debugEpString = date("Y\.m\.d", $airdate);
  } else {
    $epMatchStrings[] = 'S' . sprintf('%02d', $season) . 'E' . sprintf('%02d', $episode);
    $debugEpString = 'S' . sprintf('%02d', $season) . 'E' . sprintf('%02d', $episode);
  }
  $endMatchString = "\..*".$GLOBALS['formats'][strval($format)]."/i";

  $q_debug = stripslashes($goodShow).'*.'.$debugEpString . '.*' . $GLOBALS['formats'][strval($format)] . '';

  print_log('    searching tvbinz for ' . $q_debug);

  
  $feed_xml = simplexml_load_string($tvbinz_result);

  // search every show
  foreach ($feed_xml->channel[0]->item as $myItem) {

    // match the nzb name with our regexes
    foreach ($epMatchStrings as $curEpMatchStr) {
      $found = preg_match($startMatchString.$curEpMatchStr.$endMatchString, $myItem->title);
      if ($found)
        break;
    }

    // if we found it then return the necessary info
    if ($found) {
      print_log("      found an nzb $myItem->title at $myItem->link");
      return array('id' => $myItem->link.'&i='.$config['tvbinz_uid'].'&h='.$config['tvbinz_hash'], 'title' => $myItem->title);
    }
  }

  print_log('      show not found on rss feed from tvbinz.com');
  return false;
}

function downloadNZB($nzb, $title, $source) {
  global $config, $nzb_headers;

  // use newzbin ID
  if ($source == 'newzbin') {
    $downloaded = downloadFromNewzbin($nzb);
  // use an URL
  } else {
    $downloaded = downloadFromURL($nzb, $title, $source);
  }

  if ($downloaded === true || $downloaded === false)
    return $downloaded;
  
  $fp_nzb = fopen($config['nzb_queue'] . $downloaded[0] . '.nzb', 'w');
  $nzb_written = fwrite($fp_nzb, $downloaded[1]);
  print_log(($nzb_written ? '      NZB download successful' : '      FAIL (writing the nzb, check directory permissions)'));

  return $nzb_written;
}

function downloadFromNewzbin($nzb) {
  global $config, $configData, $nzb_headers;

  print_log('    attempting to downloading nzb from Newzbin');

  if (empty($config['newzbin_username']) || empty($config['newzbin_password'])) {
    print_log("      ERROR: Can't download from newzbin unless username and password are set");
    return false;
  }

  $nzb_headers = array();
  $ch = curl_init();
  curl_setopt_array($ch, array(
    CURLOPT_URL => 'http://www.newzbin.com/api/dnzb/',
    CURLOPT_USERAGENT => 'hellaVCR/' . $configData['version'],
    CURLOPT_POST => 1,
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_HEADER => 0, 
    CURLOPT_HEADERFUNCTION => 'read_header',
    CURLOPT_POSTFIELDS => 'username=' . $config['newzbin_username'] . '&password=' . $config['newzbin_password'] . '&reportid=' . $nzb
  ));

  $raw_nzb = curl_exec($ch);
  
  /*
  [X-DNZB-Name] =>  Eureka - 3x05 - Show Me the Mummy
  [X-DNZB-Category] =>  TV
  [X-DNZB-MoreInfo] =>  http://www.tvrage.com/Eureka/episodes/664264/3x05/
  [X-DNZB-NFO] =>  196018317
  [X-DNZB-RCode] =>  200
  [X-DNZB-RText] =>  OK, NZB content follows
	*/
  
  switch($nzb_headers['X-DNZB-RCode']) {
    case 200:
      $filename = trim($nzb_headers['X-DNZB-Name']);
      $filename = str_replace('/', ' ', $filename);
      return array($filename, $raw_nzb, $nzb_headers['X-DNZB-MoreInfo']);
    case 400:
      print_log('      FAIL (400: bad request, please supply all parameters)');
      return false;
    case 401:
      print_log('      FAIL (401: unauthorized, check username/password)');
      return false;
    case 402:
      print_log('      FAIL (402: premium account required)');
      return false;
    case 404:
      print_log('      FAIL (404: data not found)');
      return false;
    case 450:
      //api rate limited exceeded, so wait the required time and try again
      print_log('      FAIL (450: too many requests)');
      $wait_parts = explode(' ', $nzb_headers['X-DNZB-RText']);
      print_log('      waiting ' . $wait_parts[4] . ' second' . ($wait_parts[4] > 1 ? 's' : ''));
      sleep($wait_parts[4] + 1);
      return downloadFromNewzbin($nzb);
    case 500:
      print_log('      FAIL (500: internal server error)');
      return false;
    case 503:
      print_log('      FAIL (503: service unavailable, newzbin is down)');
      return false;
    default:
      print_log('      FAIL (' . trim($nzb_headers['X-DNZB-RCode']) . ': ' . trim($nzb_headers['X-DNZB-RText']) . ')');
      return false;
  }
  
  return true;
}

function downloadFromURL($nzb, $title, $source) {
  global $config, $nzb_headers;
  print_log("    attempting to downloading nzb from URL $nzb");

  $tvnzb_ch = curl_init($nzb);
  if ($source == 'tvbinz')
    curl_setopt($tvnzb_ch, CURLOPT_COOKIE, 'uid='.$config['tvbinz_uid'].'; hash='.$config['tvbinz_hash'].';');
  curl_setopt($tvnzb_ch, CURLOPT_ENCODING, 'gzip');
  curl_setopt($tvnzb_ch, CURLOPT_RETURNTRANSFER, 1);
  $raw_nzb = curl_exec($tvnzb_ch);
  $info = curl_getinfo($tvnzb_ch);
  curl_close($tvnzb_ch);
  
  switch($info['http_code']) {
    case 200:
      $filename = trim($title);
      $filename = str_replace('/', ' ', $filename);
      return array($filename, $raw_nzb);
    case 500:
      print_log('      FAIL (500: internal server error)');
      return false;
    default:
      print_log('      FAIL (ERROR ' . trim($info['http_code']).')');
      return false;
  }
  
  return true;
}

function sendToSAB($nzb, $isURL) {
  global $config;
  
  print_log('    attempting to send to sabnzbd...');

  //set params
  $authString = ((!empty($config['sabnzbd_username']) && !empty($config['sabnzbd_password'])) ? '&ma_username=' . $config['sabnzbd_username'] . '&ma_password=' . $config['sabnzbd_password'] : '');
  $modeString = ($isURL ? 'addurl' : 'addid');
  $priority = ((isset($config['sabnzbd_0.5']) && $config['sabnzbd_0.5']) ? '&priority=1' : '');

  //make curl call
  $ch = curl_init();
  curl_setopt_array($ch, array(
    CURLOPT_URL => 'http://' . $config['sabnzbd_server'] . ':' . $config['sabnzbd_port'] . '/sabnzbd/api?mode=' . $modeString . $authString . '&cat=' . $config['sabnzbd_category'] . '&pp=3&name=' . urlencode($nzb) . $priority,
    CURLOPT_HEADER => 0
  ));
  $result = curl_exec($ch);
  curl_close($ch);

  print_log($result ? '      sent to SABnzbd successfully' : '      FAIL (check sabnzbd is running and config values are correct)');
  return $result;
}

function sendToHella($nsb, $isURL) {
  global $config;
  
  print_log('    sending to hellanzb');
  
  try {
    $hc = new HellaController($config['hellanzb_server'], $config['hellanzb_port'], 'hellanzb', $config['hellanzb_password']);
  }
  //error thrown, probably since hellanzb isn't running
  catch(Exception $e) {
    print_log('      FAIL (hellanzb not running?)');
    return false;
  }
  
  //use the hellanzb class to send the id/url
  if ($isURL)
    $hc->enqueueURL($nzb);
  else
    $hc->enqueueNewzbin($nzb);
  
  //check hellanzb log to see if the id was processed successfully
  //--
  
  print_log('      nzb successfully sent to hellanzb server');
  return true;
}

function postDownload ($show, $season, $episode, $airdate, $correctFormat) {
  global $config, $configData;

  $episodeString = $season.'x'.sprintf('%02d', $episode);
  $mailString = '';
  
  if (empty($config['curl_timeout']))
    $timeout = 30;
  else
    $timeout = $config['curl_timeout'];

  // send XBMC update
  if ($config['xbmc'] && $config['xbmc_host']) {
    $xbmc_ch = curl_init("http://".$config['xbmc_host']."/xbmcCmds/xbmcHttp?command=ExecBuiltIn&parameter=Notification(Download+Started,".urlencode($show->seriesName)."+-+$episodeString)");
    curl_setopt($xbmc_ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($xbmc_ch, CURLOPT_TIMEOUT, $timeout);
    $result = curl_exec($xbmc_ch);
    curl_close($xbmc_ch);
    print_log('    XBMC notification '.($result ? 'OK':'FAILED'));
  }

  //send twitter update
  if($config['twitter'] && $twitter) {
    $prefix = $configData['hollers'][@array_rand($configData['hollers'])];
    $status = $twitter->send($prefix . $show->seriesName . ' ' . $episodeString . ' (' . $GLOBALS['formats'][strval($show->format)] . ', ' . $GLOBALS['languages'][strval($show->language)] . ')');
    print_log('    '.($status ? 'tweeted ok' : 'twitter FAILED'));
  }

  //build mail string
  if($config['mail']) {
    $mailString = $show->seriesName . ' ' . $episodeString . ' (' . $GLOBALS['formats'][strval($show->format)] . ', ' . $GLOBALS['languages'][strval($show->language)] . ")\n";
  }
  
  // only save it in history if it was the format we want
  if ($correctFormat) {


    // save it in the history
    if(!$show->showXML->downloads) $show->showXML->addChild('downloads', '');
    $download_node = $show->showXML->downloads->addChild('download', '');
    $download_node->addChild('season', $season);
    $download_node->addChild('episode', $episode);
    $download_node->addChild('timestamp', time());
    $download_node->addChild('airdate', $airdate);

    // keep the most recent DL easily accessible
    $show->dlSeason = $season;
    $show->dlEpisode = $episode;
    $show->dlTimestamp = time();
    $show->dlAirdate = $airdate;
    
    $show->dfa_waiting = 0;

    if (!$config['dfa_sticky'])
      $show->dfa = 0;

  } else {
    print_log('    first available copy has been downloaded, waiting for correct format now');
    $show->dfa_waiting = 1;
  }

  $show->saveToXML();
  
  return $mailString;

}


function read_header($ch, $string) {
	global $nzb_headers;
	$colon_index = strpos($string, ':');
	$nzb_headers[substr($string, 0, $colon_index)] = substr($string, $colon_index + 1);
	return strlen($string);
}

//sanitize a string
function sanitize_string ($string, $badchars) {

  $result_string = "";

  foreach (str_split($string) as $char) {
    if (!strstr($badchars, $char))
      $result_string .= $char;
  }

  return $result_string;
}

?>
